7.3.2. Наложение методов в CLOS и CLIPS

В языках FLAVORS и LOOPS реализованы разные механизмы комбинирования поведения, унаследованного от разных "родителей". В языке FLAVORS используется описанный выше механизм вставок, а в языке LOOPS производится дополнительное обращение к альтернативному коду.

В языке CLOS поддерживаются оба варианта. Обычно существует главный метод, который берет на себя основную часть работы по выполнению родовой операции (примером является метод refresh, о котором шла речь при обсуждении набора классов окон). Как и в языке FLAVORS, before-методы (предварительные методы) используются для подготовки данных для тех вычислений, которые должны быть выполнены primary-методом (основным методом), а after-методы (заключительные-методы) используются для выполнения заключительных операций.

Кроме того, в CLOS имеется возможность использовать так называемые around-методы (методы оболочки), которые образуют своего рода оболочку вокруг ядра (последовательности "before-метод— primary-метод— after-метод"). Такая методика предназначена для ситуаций, в которых ядро не позволяет достичь требуемого результата. Например, желательно, чтобы before-метод установил локальные переменные, которые должны быть использованы primary-методом, или когда нужно заключить primary-метод в какую-либо управляющую структуру. В ядре before- и after-методы используются только для того, чтобы сформировать побочные эффекты; возвращается же значение, сформированное primary-методом, причем это значение не ограничивается никакими внешними управляющими структурами.

Наиболее специфический around-метод связывается с сообщением, которое передается перед тем, как будут вызваны подходящие before-, primary- или after-методы. Обращение к ядру производится в процессе выполнения системной функции call-next-method, которая размещается в теле around-метода.

Стандартная методика наложения методов суммирована в схеме, представленной на рис. 7.6.

В CLOS предлагается множество дополнительных типов наложения методов. Более того, пользователь может самостоятельно создавать и собственные типы. Например, тип or-combination передает вызывающему объекту значение первого компонента, вернувшего значение, отличное от NIL. При создании собственных функций наложения пользователь может использовать операторы разных видов: логические, арифметические или манипулирования списками.

Следует отметить, что стандартная схема наложения методов удовлетворяет потребности программирования практически на 90%.

Методы в CLOS являются эффективными родовыми функциями, возможность применения которых зависит от специальных параметров, представляющих класс первого аргумента сообщения. Методы вызываются точно так же, как и функции LISP (т.е. не используются никакие функции отсылки, как это делается в LOOPS). Получатель сообщения представляется первым аргументом, а остальные'аргументы — обычные параметры функции.

Рис. 7.6. Стандартная схема наложения методов в языках CLOS и CLIPS ([Keene,1989])

Кроме того, в CLOS существуют и так называемые мультиметоды, которые позволяют настраивать поведение в зависимости от классов нескольких аргументов, а не одного. Например, люди, принадлежащие к разным культурам, не только отдают в своем рационе приоритет разным продуктам, но и готовят их по-разному. Так, японцы, в отличие от американцев, отдают предпочтение рыбе, но, кроме того, они часто едят рыбу сырой. Таким образом, метод prepare-meal (приготовить пищу) должен быть чувствителен как к национальности получателя сообщения, так и к виду предлагаемого блюда. Метод имеет два аргумента

(prepare-meal X Y)

и его реализация зависит как от класса аргумента X (повара), так и от класса аргумента У (блюда). Аналогичные возможности в CLIPS обеспечиваются посредством родовых функций (подробнее об этом — в главе 17).

7.3. Как сделать людей вежливыми

Положим, что используются те же обработчики сообщений для классов guaker и republican, что и в предыдущем примере. Можно, определив специальный заключительный метод для класса person, придать формируемым ответам вежливый вид. Этот метод будет выполняться после того, как будет выполнен основной метод, выбранный для ответа на сообщение speak:

(defmessage-handler guaker speak ()

(printout t crlf "Peace")

)

(defmessage-handler republican speak ()

(printout t crlf "War")

)

(defmessage-handler person speak after()

(printout t ", please" t crlf)

)

Теперь в ответ на запрос (send frichard] speak) последует ответ "War, please" Обработчики сообщений базовых классов имеют статус primary по умолчанию, а потому можно и не указывать это явно в объявлении обработчика, как это сделано, например, ниже:

(defmessage-handler republican speak primary () (printout t crlf "War")